%{

#include "structures.hpp"
#include "actions.hpp"

#include "ProgramManager.hpp"
#include "CallingContext.hpp"

#include "Instruction.hpp"
#include "InstructionFactory.hpp"

#include "Expression.hpp"
#include "ExpressionFactory.hpp"

extern int yyerror ( const char* m );
extern int yylex ();

%}

%union{
    int vboolean;
    int vinteger;
    float vreal;
    char vstring [ 512 ];
    unsigned int vindex;
    IdentificateurModifie videntmod;
    ListeIdentificateurModifie vlistidentmod;
    BaseType vtypebase;
    Instruction* vinstruction;
    ListeInstruction* vlinstruction;
    Expression* vexpression;
    ListeExpression* vlexpression;
}

%token KW_INTEGER
%token KW_REAL
%token KW_VOID
%token KW_BOOLEAN
%token KW_IF
%token KW_ELSE
%token KW_FOR
%token KW_WHILE
%token KW_DO
%token KW_WRITE
%token KW_READ
%token KW_RETURN

%token PAR_OUVRANTE
%token PAR_FERMANTE
%token ACC_OUVRANTE
%token ACC_FERMANTE
%token CRO_OUVRANT
%token CRO_FERMANT

%token SEP_PVIRGULE
%token SEP_VIRGULE

%token OP_ADD
%token OP_SUB
%token OP_MUL
%token OP_DIV
%token OP_LT
%token OP_LTE
%token OP_GT
%token OP_GTE
%token OP_EQ
%token OP_NEQ
%token OP_AND
%token OP_OR
%token OP_NOT
%token OP_ADR
%token OP_AFFECT

%token < vboolean > CST_BOOLEAN
%token < vinteger > CST_INTEGER
%token < vreal > CST_REAL
%token < vindex > CST_IDENTIFICATOR
%token < vstring > CST_STRING

%type < vlistidentmod > VN_ListeIdentificateursModifies
%type < videntmod > VN_IdentificateurModifie
%type < vtypebase > VN_TypeBase

%type < vinstruction > VN_Instruction
%type < vinstruction > VN_InstructionFlot
%type < vinstruction > VN_InstructionSimple
%type < vinstruction > VN_InstructionConditionnelle
%type < vinstruction > VN_InstructionBoucle
%type < vinstruction > VN_InstructionFor
%type < vinstruction > VN_InstructionWhile
%type < vinstruction > VN_InstructionDoWhile
%type < vinstruction > VN_InstructionAffectation
%type < vinstruction > VN_InstructionAppel
%type < vinstruction > VN_InstructionEntreeSortie
%type < vinstruction > VN_BlocInstructions

%type < vinstruction > VN_CorpsFonction

%type < vlinstruction > VN_ListeInstructions

%type < vexpression > VN_Expression
%type < vexpression > VN_ExpressionMathematique
%type < vexpression > VN_ExpressionBooleenne
%type < vexpression > VN_ExpressionComparaison
%type < vexpression > VN_ExpressionAutres
%type < vexpression > VN_ExpressionAtomique

%type < vlexpression > VN_ListeExpressions
%type < vlexpression > VN_ResteListeExpressions

%start VN_Program

%nonassoc OP_EQ OP_NEQ OP_GT OP_LT OP_GTE OP_LTE
%left OP_ADD OP_SUB OP_OR
%left OP_MUL OP_DIV OP_AND
%right OP_NOT OP_NEG
%nonassoc OP_ADR OP_DEREF
%left CRO_OUVRANT

%nonassoc KW_IFX
%nonassoc KW_ELSE

%%

VN_Program                      :   VN_ListeFonctions
                                ;

VN_ListeFonctions               :   VN_DefinitionFonction VN_ListeFonctions
                                |   VN_DefinitionFonction
                                ;

VN_DefinitionFonction           :   VN_EnteteFonction VN_CorpsFonction { ProgramManager::getCurrentContext ()->setCode ( $2 ); }
                                ;

VN_EnteteFonction               :   VN_TypeBase CST_IDENTIFICATOR {
                                        ProgramManager::createCallingContext ( $2 );
                                        ProgramManager::getCurrentContext ()->setReturnType ( $1 );
                                    } PAR_OUVRANTE VN_ListeParametresFormels PAR_FERMANTE
                                ;

VN_ListeParametresFormels       :   VN_ParametreFormel VN_ResteListeParametresFormels
                                |
                                ;

VN_ResteListeParametresFormels  :   SEP_VIRGULE VN_ParametreFormel VN_ResteListeParametresFormels
                                |
                                ;

VN_ParametreFormel              :   VN_TypeBase VN_IdentificateurModifie {
                                                    Type type;
                                                    type._base = $1;
                                                    type._modifier = $2.vmodifier;
                                                    type._size = $2.vsize;
                                                    ProgramManager::getCurrentContext ()->addParameter ( $2.vindex, type );
                                    }
                                ;

VN_CorpsFonction                :   ACC_OUVRANTE VN_DeclarationVariables VN_ListeInstructions ACC_FERMANTE { $$ = InstructionFactory::generateBloc ( $3->elements ); }
                                |   ACC_OUVRANTE VN_ListeInstructions ACC_FERMANTE { $$ = InstructionFactory::generateBloc ( $2->elements ); }
                                |   ACC_OUVRANTE VN_DeclarationVariables ACC_FERMANTE { $$ = NULL; }
                                |   ACC_OUVRANTE ACC_FERMANTE { $$ = NULL; }
                                ;

VN_DeclarationVariables         :   VN_DeclarationVariables VN_DeclarationVariable
                                |   VN_DeclarationVariable
                                ;

VN_DeclarationVariable          :   VN_TypeBase VN_ListeIdentificateursModifies SEP_PVIRGULE {
                                        Type type;
                                        type._base = $1;
                                        for ( unsigned int i = 0 ; i < $2.count ; i ++ )
                                        {
                                            type._modifier = $2.elements [ i ].vmodifier;
                                            type._size = $2.elements [ i ].vsize;
                                            ProgramManager::getCurrentContext ()->addVariable ( $2.elements [ i ].vindex, type );
                                        }
                                    }
                                ;

VN_ListeIdentificateursModifies :   VN_IdentificateurModifie SEP_VIRGULE VN_ListeIdentificateursModifies {
                                        $$.elements = (IdentificateurModifie*) realloc ( $3.elements, ( $3.count + 1 ) * sizeof ( IdentificateurModifie ) );
                                        $$.count = $3.count + 1;
                                        $$.elements [ $$.count - 1 ] = $1;
                                    }
                                |   VN_IdentificateurModifie {
                                        $$.elements = (IdentificateurModifie*) malloc ( 1 * sizeof ( IdentificateurModifie ) );
                                        $$.count = 1;
                                        $$.elements [ $$.count - 1 ] = $1;
                                    }
                                ;

VN_IdentificateurModifie        :   CST_IDENTIFICATOR {
                                        $$.vmodifier = NONE;
                                        $$.vsize = 0;
                                        $$.vindex = $1;
                                    }
                                |   OP_MUL CST_IDENTIFICATOR {
                                        $$.vmodifier = POINTER;
                                        $$.vsize = 0;
                                        $$.vindex = $2;
                                    }
                                |   CST_IDENTIFICATOR CRO_OUVRANT CST_INTEGER CRO_FERMANT {
                                        $$.vmodifier = ARRAY;
                                        $$.vsize = $3;
                                        $$.vindex = $1;
                                    }
                                ;

VN_ListeInstructions            :   VN_ListeInstructions VN_Instruction { $$ = $1; $$->elements.push_back ( $2 ); }
                                |   VN_Instruction { $$ = new ListeInstruction; $$->elements.push_back ( $1 ); }
                                ;

VN_Instruction                  :   VN_InstructionFlot { $$ = $1; }
                                |   VN_InstructionSimple { $$ = $1; }
                                |   VN_BlocInstructions { $$ = $1; }
                                ;

VN_InstructionFlot              :   VN_InstructionConditionnelle { $$ = $1; }
                                |   VN_InstructionBoucle { $$ = $1; }
                                ;

VN_InstructionSimple            :   VN_InstructionAffectation SEP_PVIRGULE { $$ = $1; }
                                |   VN_InstructionAppel SEP_PVIRGULE { $$ = $1; }
                                |   VN_InstructionEntreeSortie SEP_PVIRGULE { $$ = $1; }
                                ;

VN_InstructionConditionnelle    :   KW_IF PAR_OUVRANTE VN_Expression PAR_FERMANTE VN_Instruction %prec KW_IFX { $$ = InstructionFactory::generateIfThen ( $3, $5 ); }
                                |   KW_IF PAR_OUVRANTE VN_Expression PAR_FERMANTE VN_Instruction KW_ELSE VN_Instruction { $$ = InstructionFactory::generateIfThenElse ( $3, $5, $7 ); }
                                ;

VN_InstructionBoucle            :   VN_InstructionFor { $$ = $1; }
                                |   VN_InstructionWhile { $$ = $1; }
                                |   VN_InstructionDoWhile SEP_PVIRGULE { $$ = $1; }
                                ;

VN_InstructionFor               :   KW_FOR PAR_OUVRANTE VN_InstructionAffectation SEP_PVIRGULE
                                        VN_Expression SEP_PVIRGULE
                                        VN_InstructionAffectation PAR_FERMANTE VN_Instruction { $$ = InstructionFactory::generateFor ( $3, $5, $7, $9 ); }
                                ;

VN_InstructionWhile             :   KW_WHILE PAR_OUVRANTE VN_Expression PAR_FERMANTE VN_Instruction { $$ = InstructionFactory::generateWhile ( $3, $5 ); }
                                ;

VN_InstructionDoWhile           :   KW_DO VN_Instruction KW_WHILE PAR_OUVRANTE VN_Expression PAR_FERMANTE { $$ = InstructionFactory::generateRepeat ( $5, $2 ); }
                                ;

VN_InstructionAffectation       :   VN_Expression OP_AFFECT VN_Expression { $$ = InstructionFactory::generateAffectation ( $1, $3 ); }
                                ;

VN_InstructionAppel             :   CST_IDENTIFICATOR PAR_OUVRANTE VN_ListeExpressions PAR_FERMANTE { $$ = InstructionFactory::generateCall ( ProgramManager::getNameOf ( $1 ), $3->elements ); }
                                |   KW_RETURN VN_Expression { $$ = InstructionFactory::generateReturn ( $2 ); }
                                |   KW_RETURN { $$ = InstructionFactory::generateReturn ( NULL ); }
                                ;

VN_InstructionEntreeSortie      :   KW_READ PAR_OUVRANTE VN_ListeExpressions PAR_FERMANTE { $$ = InstructionFactory::generateInput ( $3->elements ); }
                                |   KW_WRITE PAR_OUVRANTE VN_ListeExpressions PAR_FERMANTE { $$ = InstructionFactory::generateOutput ( $3->elements ); }
                                ;

VN_BlocInstructions             :   ACC_OUVRANTE VN_ListeInstructions ACC_FERMANTE { $$ = InstructionFactory::generateBloc ( $2->elements ); }
                                |   ACC_OUVRANTE ACC_FERMANTE { $$ = NULL; }
                                ;

VN_ListeExpressions             :   VN_Expression VN_ResteListeExpressions { $$ = $2; $$->elements.insert ( $$->elements.begin (), $1 ); }
                                |   { $$ = new ListeExpression; }
                                ;

VN_ResteListeExpressions        :   SEP_VIRGULE VN_Expression VN_ResteListeExpressions { $$ = $3; $$->elements.insert ( $$->elements.begin (), $2 ); }
                                |   { $$ = new ListeExpression; }
                                ;

VN_Expression                   :   VN_ExpressionMathematique { $$ = $1; }
                                |   VN_ExpressionBooleenne { $$ = $1; }
                                |   VN_ExpressionComparaison { $$ = $1; }
                                |   VN_ExpressionAutres { $$ = $1; }
                                |   VN_ExpressionAtomique { $$ = $1; }
                                ;

VN_ExpressionMathematique       :   VN_Expression OP_ADD VN_Expression { $$ = ExpressionFactory::generateAdd ( $1, $3 ); }
                                |   VN_Expression OP_SUB VN_Expression { $$ = ExpressionFactory::generateSub ( $1, $3 ); }
                                |   VN_Expression OP_MUL VN_Expression { $$ = ExpressionFactory::generateMul ( $1, $3 ); }
                                |   VN_Expression OP_DIV VN_Expression { $$ = ExpressionFactory::generateDiv ( $1, $3 ); }
                                |   OP_SUB VN_Expression %prec OP_NEG { $$ = ExpressionFactory::generateMinus ( $2 ); }
                                ;

VN_ExpressionBooleenne          :   VN_Expression OP_AND VN_Expression { $$ = ExpressionFactory::generateAnd ( $1, $3 ); }
                                |   VN_Expression OP_OR VN_Expression { $$ = ExpressionFactory::generateOr ( $1, $3 ); }
                                |   OP_NOT VN_Expression { $$ = ExpressionFactory::generateNot ( $2 ); }
                                ;

VN_ExpressionComparaison        :   VN_Expression OP_LT VN_Expression { $$ = ExpressionFactory::generateLt ( $1, $3 ); }
                                |   VN_Expression OP_LTE VN_Expression { $$ = ExpressionFactory::generateLte ( $1, $3 ); }
                                |   VN_Expression OP_GT VN_Expression { $$ = ExpressionFactory::generateGt ( $1, $3 ); }
                                |   VN_Expression OP_GTE VN_Expression { $$ = ExpressionFactory::generateGte ( $1, $3 ); }
                                |   VN_Expression OP_EQ VN_Expression { $$ = NULL; }
                                |   VN_Expression OP_NEQ VN_Expression { $$ = NULL; }
                                ;

VN_ExpressionAutres             :   PAR_OUVRANTE VN_Expression PAR_FERMANTE { $$ = $2; }
                                |   CST_IDENTIFICATOR PAR_OUVRANTE VN_ListeExpressions PAR_FERMANTE { $$ = ExpressionFactory::generateCall(ProgramManager::getNameOf( $1 ), $3->elements); }
                                |   OP_MUL VN_Expression %prec OP_DEREF { $$ = ExpressionFactory::generateDeref ( $2 ); }
                                |   OP_ADR VN_Expression { $$ = ExpressionFactory::generateAddress ( $2 ); }
                                |   VN_Expression CRO_OUVRANT VN_Expression CRO_FERMANT { $$ = ExpressionFactory::generateArray ( $1, $3 ); }
                                ;

VN_ExpressionAtomique           :   CST_BOOLEAN { $$ = ExpressionFactory::generateConstant ( $1 ); }
                                |   CST_INTEGER { $$ = ExpressionFactory::generateConstant ( $1 ); }
                                |   CST_REAL { $$ = ExpressionFactory::generateConstant ( $1 ); }
                                |   CST_STRING { $$ = ExpressionFactory::generateConstant ( $1 ); }
                                |   CST_IDENTIFICATOR { $$ = ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( $1 ) ); }
                                ;

VN_TypeBase                     :   KW_INTEGER { $$ = INTEGER; }
                                |   KW_REAL { $$ = REAL; }
                                |   KW_BOOLEAN { $$ = BOOLEAN; }
                                |   KW_VOID { $$ = VOID; }
                                ;

%%
